package com.zw.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zw.dto.BudgetDto;
import com.zw.dto.BudgetSortDto;
import com.zw.dto.ImportBudgetDto;
import com.zw.entity.Budget;
import com.zw.entity.BudgetColumnsContent;
import com.zw.entity.Project;
import com.zw.entity.TemplateColumns;
import com.zw.enumeration.OperationType;
import com.zw.exception.ServiceException;
import com.zw.mapper.BudgetColumnsContentMapper;
import com.zw.mapper.BudgetMapper;
import com.zw.mapper.ProjectMapper;
import com.zw.mapper.TemplateColumnsMapper;
import com.zw.service.BudgetService;
import com.zw.service.ProjectService;
import com.zw.utils.ReduceOperationUtil;
import com.zw.utils.TypeConversionUtil;
import com.zw.vo.BudgetCalculationVo;
import com.zw.vo.BudgetVo;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.Comparator;

@Service
public class BudgetServiceImpl extends ServiceImpl<BudgetMapper, Budget> implements BudgetService {
    @Resource
    private BudgetMapper budgetMapper;
    @Resource
    private TemplateColumnsMapper templateColumnsMapper;
    @Resource
    private BudgetColumnsContentMapper budgetColumnsContentMapper;
    @Resource
    private ProjectMapper projectMapper;


    /**
     * 删除预算项
     *
     * @param id
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeBudget(Long id) {
        Budget budget = baseMapper.selectById(id);
        if (Objects.isNull(budget)) return;
        // 查询所有预算项 包括子级预算项
        List<Long> ids = this.listSubTree(budget, true);
        // 删除预算行
        this.removeBatchByIds(ids);
        // 删除预算列内容
        ids.forEach(budgetId -> {
            budgetColumnsContentMapper.delete(Wrappers.lambdaQuery(BudgetColumnsContent.class)
                    .eq(BudgetColumnsContent::getBudgetId, budgetId)
            );
        });
        // 更新父级预算值
        this.recursionUpdateParentBudget(budget);
        // 更新项目预算项和预算额
        Project project = projectMapper.selectById(budget.getProjectId());
        project.setBudgetItem(project.getBudgetItem() - ids.size());
        List<Budget> budgetList = budgetMapper.selectList(Wrappers.lambdaQuery(Budget.class)
                .eq(Budget::getProjectId, budget.getProjectId())
                .eq(Budget::getTemplateId, budget.getTemplateId())
                .eq(Budget::getType, 0)
                .eq(Budget::getParentId, 0)
        );
        project.setBudgetSum(budgetList.stream().map(item -> item.getBudget() == null ? BigDecimal.ZERO : item.getBudget()).reduce(BigDecimal::add).orElse(BigDecimal.ZERO));
        projectMapper.updateById(project);
    }

    @Override
    public Budget saveBudget(Budget budget) {
        budgetMapper.insert(budget);
        return budget;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateBudget(Budget budgetDto) {
        Budget budget = baseMapper.selectById(budgetDto.getId());
        if (Objects.isNull(budget)) return;
        // 更新当前预算项
        baseMapper.updateById(budgetDto);
        // 更新父级的预算项
        recursionUpdateParentBudget(baseMapper.selectById(budget.getId()));
        // 更新项目的预算额和预算项
        List<Budget> budgetList = baseMapper.selectList(Wrappers.lambdaQuery(Budget.class)
                .eq(Budget::getProjectId, budget.getProjectId())
                .eq(Budget::getType, 0)
        );
        double sum = budgetList.stream()
                .filter(item -> item.getParentId() == 0)
                .mapToDouble(item -> item.getBudget() == null ? 0.0 : item.getBudget().doubleValue())
                .sum();
        projectMapper.updateById(Project.builder()
                .id(budget.getProjectId())
                .budgetItem(budgetList.size())
                .budgetSum(new BigDecimal(sum))
                .build()
        );
        // 更新小节预算
        recalculateBudgetForTypeOne(budget.getProjectId(), budget.getParentId(), budget.getTemplateId());
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void importBudget(List<ImportBudgetDto> importBudgetDtos) {
        if (CollectionUtils.isEmpty(importBudgetDtos)) return;
        for (ImportBudgetDto importBudgetDto : importBudgetDtos) {
            // 新增行数据
            Budget budget = BeanUtil.copyProperties(importBudgetDto, Budget.class);
            baseMapper.insert(budget);
            List<BudgetColumnsContent> columnsContent = importBudgetDto.getColumnsContent();
            if (CollectionUtils.isEmpty(columnsContent)) continue;
            // 新增列数据
            for (BudgetColumnsContent budgetColumnsContent : columnsContent) {
                budgetColumnsContent.setBudgetId(budget.getId());
                budgetColumnsContent.setRowId(budget.getRowId());
            }
            budgetColumnsContentMapper.insert(columnsContent);
        }
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeByProjectId(Long projectId) {
        List<Long> budgetIds = this.list(Wrappers.lambdaQuery(Budget.class).eq(Budget::getProjectId, projectId))
                .stream().map(Budget::getId).toList();
        this.removeBatchByIds(budgetIds);
        budgetIds.forEach(budgetId -> {
            budgetColumnsContentMapper.delete(Wrappers.lambdaQuery(BudgetColumnsContent.class)
                    .eq(BudgetColumnsContent::getBudgetId, budgetId)
            );
        });
    }

    // 递归更新父级预算项
    private void recursionUpdateParentBudget(Budget budget) {
        if (budget == null || Objects.equals(budget.getParentId(), 0L)) return;

        if (budget.getParentId() != null) {
            List<Budget> parentBudgets = baseMapper.selectList(Wrappers.lambdaQuery(Budget.class)
                    .eq(Budget::getProjectId, budget.getProjectId())
                    .eq(Budget::getParentId, budget.getParentId())
                    .eq(Budget::getType, 0)
            );
            double sum = parentBudgets.stream()
                    .mapToDouble(item -> item.getBudget() == null ? 0.0 : item.getBudget().doubleValue())
                    .sum();
            // 更新父级预算项
            Budget parentBudgetItem = new Budget();
            parentBudgetItem.setId(budget.getParentId());
            parentBudgetItem.setBudget(new BigDecimal(sum));
            baseMapper.updateById(parentBudgetItem);

            // 递归更新父级预算项
            recursionUpdateParentBudget(baseMapper.selectById(budget.getParentId()));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateBudgetSort(List<BudgetSortDto> sortRequests) {
        // 1. 更新排序
        sortRequests.forEach(sortRequest -> {
            Optional.ofNullable(baseMapper.selectById(sortRequest.getId()))
                    .ifPresent(budget -> {
                        budget.setSort(sortRequest.getSort());
                        baseMapper.updateById(budget);
                    });
        });

        // 2. 重新计算type为1的预算值
        // 获取涉及的项目ID和模板ID，用于查询相关的预算数据
        if (!sortRequests.isEmpty()) {
            Long firstBudgetId = sortRequests.get(0).getId();
            Budget firstBudget = baseMapper.selectById(firstBudgetId);
            if (firstBudget != null) {
                recalculateBudgetForTypeOne(firstBudget.getProjectId(), firstBudget.getParentId(), firstBudget.getTemplateId());
            }
        }
    }

    /**
     * 获取预算项详情信息
     */
    @Override
    public List<BudgetVo> getBudgetDetail(BudgetDto budgetDto) {
        Long templateId = budgetDto.getTemplateId();
        boolean showChildren = Boolean.TRUE.equals(budgetDto.getShowChildren());

        // 1. 查询根级预算项
        List<Budget> rootBudgets = budgetMapper.selectByDto(budgetDto);
        if (CollectionUtils.isEmpty(rootBudgets)) {
            return Collections.emptyList();
        }

        // 查询模板列定义
        List<TemplateColumns> templateColumns = templateColumnsMapper.selectTemplateClomns(templateId);
        Map<Long, TemplateColumns> columnDefinitionMap = templateColumns.stream()
                .collect(Collectors.toMap(TemplateColumns::getId, Function.identity()));

        // 如果需要查询子项，则递归加载所有后代
        if (showChildren) {
            // 获取所有相关的预算项（包括子项），避免 N+1 查询
            List<Budget> allBudgets = getAllBudgetsWithChildren(rootBudgets, templateId);
            // 构建 id -> budget 映射
            Map<Long, Budget> budgetMap = allBudgets.stream()
                    .collect(Collectors.toMap(Budget::getId, Function.identity()));
            // 构建 parentId -> children 映射
            Map<Long, List<Budget>> childrenMap = allBudgets.stream()
                    .filter(b -> b.getParentId() != 0)
                    .collect(Collectors.groupingBy(Budget::getParentId));

            // 获取所有 budgetIds 用于后续查询列内容
            List<Long> allBudgetIds = allBudgets.stream().map(Budget::getId).collect(Collectors.toList());
            List<BudgetColumnsContent> columnContents = budgetMapper.selectColumnContent(allBudgetIds);
            Map<Long, List<BudgetColumnsContent>> contentMap = columnContents.stream()
                    .collect(Collectors.groupingBy(BudgetColumnsContent::getBudgetId));

            // 从根节点开始构建树形结构
            return rootBudgets.stream()
                    .map(root -> buildBudgetVoTree(root, budgetMap, childrenMap, contentMap, columnDefinitionMap))
                    .collect(Collectors.toList());
        } else {
            List<Long> budgetIds = rootBudgets.stream().map(Budget::getId).collect(Collectors.toList());
            List<BudgetColumnsContent> columnContents = budgetMapper.selectColumnContent(budgetIds);
            Map<Long, List<BudgetColumnsContent>> contentMap = columnContents.stream()
                    .collect(Collectors.groupingBy(BudgetColumnsContent::getBudgetId));

            return rootBudgets.stream()
                    .map(budget -> convertToVo(budget, contentMap, columnDefinitionMap))
                    .collect(Collectors.toList());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOrUpdateBudgets(List<BudgetColumnsContent> budgetItems) {
        for (BudgetColumnsContent budgetItem : budgetItems) {
            Budget budget = new Budget();
            budget.setId(budgetItem.getBudgetId());
            budget.setRowId(budgetItem.getRowId());
            budgetMapper.updateById(budget);
            if (Objects.isNull(budgetItem.getId())) {
                budgetColumnsContentMapper.insert(budgetItem);
            } else {
                budgetColumnsContentMapper.updateById(budgetItem);
            }
            // 递归更新父级相应的字段
            recalculateOperationField(budgetItem);
        }
    }

    /**
     * 递归更新父级相应的字段
     */
    private void recalculateOperationField(BudgetColumnsContent budgetItem) {
        // 获取当前预算行
        Budget budget = budgetMapper.selectById(budgetItem.getBudgetId());
        // 如果是最顶级预算行，不需要操作
        if (budget.getParentId() == 0) return;

        // 获取模板列定义
        TemplateColumns templateColumns = templateColumnsMapper.selectOne(Wrappers.lambdaQuery(TemplateColumns.class)
                .eq(TemplateColumns::getTemplateId, budget.getTemplateId())
                .eq(TemplateColumns::getId, budgetItem.getColumnsId())
        );
        // 是否为操作列
        if (Objects.isNull(templateColumns) || templateColumns.getOperationType().equals(OperationType.KEEP.getCode()))
            return;
        // 获取父级行 单元格
        BudgetColumnsContent parentItem = budgetColumnsContentMapper.selectOne(Wrappers.lambdaQuery(BudgetColumnsContent.class)
                .eq(BudgetColumnsContent::getBudgetId, budget.getParentId())
                .eq(BudgetColumnsContent::getColumnsId, templateColumns.getId())
        );
        // 如果父级项不存在，可能需要创建
        if (parentItem == null) {
            parentItem = new BudgetColumnsContent();
            parentItem.setBudgetId(budget.getParentId());
            parentItem.setRowId(budget.getRowId());
            parentItem.setColumnsId(templateColumns.getId());
            parentItem.setContent("0"); // 初始值
            budgetColumnsContentMapper.insert(parentItem);
        }
        // 获取当前父级的所有子级预算行
        List<Budget> childBudgets = budgetMapper.selectList(Wrappers.lambdaQuery(Budget.class)
                .eq(Budget::getParentId, budget.getParentId()));
        // 找到所有子级行的对应列内容
        List<BudgetColumnsContent> childColumnsContents = budgetColumnsContentMapper.selectList(
                Wrappers.lambdaQuery(BudgetColumnsContent.class)
                        .in(BudgetColumnsContent::getBudgetId,
                                childBudgets.stream().map(Budget::getId).collect(Collectors.toList()))
                        .eq(BudgetColumnsContent::getColumnsId, templateColumns.getId())
        );
        if (!childColumnsContents.isEmpty()) {
            // 计算所有子级的列内容和
            Optional<Object> sumOptional = childColumnsContents.stream()
                    .map(item -> TypeConversionUtil.convert(item.getContent(), templateColumns.getColumnType()))
                    .reduce(ReduceOperationUtil.getReduceOperator(templateColumns.getColumnType()));

            if (sumOptional.isPresent()) {
                Object sum = sumOptional.get();
                String newContent;

                if (templateColumns.getOperationType().equals(OperationType.SUM.getCode())) {
                    // 如果是相加操作
                    newContent = String.valueOf(sum);
                } else if (templateColumns.getOperationType().equals(OperationType.AVERAGE.getCode())) {
                    // 如果是平均值操作
                    int count = childColumnsContents.size();

                    // 根据类型计算平均值
                    switch (templateColumns.getColumnType()) {
                        case "int":
                            Integer intSum = (Integer) sum;
                            double intAvg = intSum.doubleValue() / count;
                            newContent = String.valueOf(Math.round(intAvg)); // 四舍五入
                            break;
                        case "bigint":
                            Long longSum = (Long) sum;
                            double longAvg = longSum.doubleValue() / count;
                            newContent = String.valueOf(Math.round(longAvg)); // 四舍五入
                            break;
                        case "float":
                            Float floatSum = (Float) sum;
                            float floatAvg = floatSum / count;
                            newContent = String.format("%.2f", floatAvg);
                            break;
                        case "double":
                            Double doubleSum = (Double) sum;
                            double doubleAvg = doubleSum / count;
                            newContent = String.format("%.2f", doubleAvg);
                            break;
                        default:
                            throw new IllegalArgumentException("不支持的列类型: " + templateColumns.getColumnType());
                    }
                } else {
                    // 如果不是SUM或AVERAGE，保持原值
                    newContent = parentItem.getContent();
                }

                // 只有当内容发生变化时才更新
                if (!newContent.equals(parentItem.getContent())) {
                    parentItem.setContent(newContent);
                    budgetColumnsContentMapper.updateById(parentItem);

                    // 递归更新父级的父级
                    recalculateOperationField(parentItem);
                }
            }
        }
    }

    /**
     * 递归获取子项
     */
    private List<Long> listSubTree(Budget budget, boolean hasSelf) {
        // 查询所有预算行
        List<Budget> all = this.list(Wrappers.lambdaQuery(Budget.class)
                .eq(Budget::getTemplateId, budget.getTemplateId())
                .eq(Budget::getProjectId, budget.getProjectId())
        );
        // 格式转换
        Map<Long, List<Long>> graph = all.stream()
                .collect(Collectors.groupingBy(Budget::getParentId,
                        Collectors.mapping(Budget::getId, Collectors.toList())));
        // BFS 展开
        List<Long> result = new ArrayList<>();
        Deque<Long> queue = new ArrayDeque<>(graph.getOrDefault(budget.getId(), Collections.emptyList()));
        while (!queue.isEmpty()) {
            Long curr = queue.poll();
            result.add(curr);
            queue.addAll(graph.getOrDefault(curr, Collections.emptyList()));
        }
        // 是否追加自己
        if (hasSelf) {
            result.add(0, budget.getId());
        }

        // 所有的预算行 id
        return result;
    }

    /**
     * 递归获取所有子项
     */
    private List<Budget> getAllBudgetsWithChildren(List<Budget> budgets, Long templateId) {
        List<Budget> result = new ArrayList<>(budgets);
        List<Long> currentIds = budgets.stream().map(Budget::getId).collect(Collectors.toList());

        while (!currentIds.isEmpty()) {
            // 查询当前层级的所有直接子项
            BudgetDto childDto = new BudgetDto();
            childDto.setTemplateId(templateId);
            childDto.setParentIds(currentIds);
            List<Budget> children = budgetMapper.selectByParentIds(childDto);
            if (CollectionUtils.isEmpty(children)) {
                break;
            }
            result.addAll(children);
            currentIds = children.stream().map(Budget::getId).collect(Collectors.toList());
        }
        return result;
    }

    /**
     * 递归构建 BudgetVo 树（使用预加载的映射避免重复查询）
     */
    private BudgetVo buildBudgetVoTree(Budget budget,
                                       Map<Long, Budget> budgetMap,
                                       Map<Long, List<Budget>> childrenMap,
                                       Map<Long, List<BudgetColumnsContent>> contentMap,
                                       Map<Long, TemplateColumns> columnDefinitionMap) {
        BudgetVo vo = convertToVo(budget, contentMap, columnDefinitionMap);

        // 递归处理子项
        List<Budget> children = childrenMap.get(budget.getId());
        if (!CollectionUtils.isEmpty(children)) {
            List<BudgetVo> childVos = children.stream()
                    .map(child -> buildBudgetVoTree(child, budgetMap, childrenMap, contentMap, columnDefinitionMap))
                    .collect(Collectors.toList());
            vo.setChildren(childVos);
        } else {
            vo.setChildren(Collections.emptyList());
        }
        return vo;
    }


    /**
     * 转换为VO
     *
     * @param budget
     * @param contentMap
     * @param columnDefinitionMap
     * @return
     */
    private BudgetVo convertToVo(Budget budget,
                                 Map<Long, List<BudgetColumnsContent>> contentMap,
                                 Map<Long, TemplateColumns> columnDefinitionMap) {
        BudgetVo vo = BeanUtil.copyProperties(budget, BudgetVo.class);
        // 处理列内容
        List<BudgetColumnsContent> contents = contentMap.getOrDefault(budget.getId(), Collections.emptyList());
        for (BudgetColumnsContent content : contents) {
            // 根据columnsId获取列定义
            TemplateColumns columnDef = columnDefinitionMap.get(content.getColumnsId());
            if (columnDef != null) {
                vo.getColNameToContent().put(columnDef.getColumn(), content);
                // 设置可见性和固定属性
                vo.setVisible(columnDef.getVisible());
                vo.setFixed(columnDef.getFixed());
            }
        }

        return vo;
    }

    /**
     * 重新计算type为1的预算值
     * 规则：
     * 1. type为1的budget值应为之前所有type非1的budget总和
     * 2. 如果存在多个type为1的分段，则只计算中间type=0的预算总和
     *
     * @param projectId  项目ID
     * @param templateId 模板ID
     */
    private void recalculateBudgetForTypeOne(Long projectId, Long parentId, Long templateId) {
        // 查询该项目模板下的所有预算项，按sort排序
        BudgetDto queryDto = new BudgetDto();
        queryDto.setProjectId(projectId);
        queryDto.setParentId(parentId);
        queryDto.setTemplateId(templateId);
        List<Budget> allBudgets = budgetMapper.selectByDto(queryDto);

        if (CollectionUtils.isEmpty(allBudgets)) {
            return;
        }

        // 按sort排序确保顺序正确
        allBudgets.sort(Comparator.comparing(Budget::getSort, Comparator.nullsLast(Integer::compareTo)));

        // 遍历所有预算项，重新计算type为1的预算值
        for (int i = 0; i < allBudgets.size(); i++) {
            Budget currentBudget = allBudgets.get(i);

            // 只处理type为1的预算项
            if (currentBudget.getType() != null && currentBudget.getType() == 1) {
                BudgetCalculationVo budgetCalculationVo = calculateBudgetForTypeOne(allBudgets, i);

                // 更新预算值
                currentBudget.setBudget(budgetCalculationVo.getTotalBudget());
                // 更新名称
                currentBudget.setContent(budgetCalculationVo.getStartRowId() + " 至 " + budgetCalculationVo.getEndRowId() + " 小节");
                baseMapper.updateById(currentBudget);
            }
        }
    }

    /**
     * 计算type为1预算项的budget值
     *
     * @param allBudgets   所有预算项列表（已排序）
     * @param currentIndex 当前type为1预算项的索引
     * @return 计算后的budget值
     */
    private BudgetCalculationVo calculateBudgetForTypeOne(List<Budget> allBudgets, int currentIndex) {
        BigDecimal totalBudget = BigDecimal.ZERO;
        String startRowId = null;
        String endRowId = null;

        // 查找上一个type为1的位置（如果存在）
        int startIndex = 0;
        for (int i = currentIndex - 1; i >= 0; i--) {
            Budget budget = allBudgets.get(i);
            if (budget.getType() != null && budget.getType() == 1) {
                startIndex = i + 1; // 从上一个type为1的下一个位置开始计算
                break;
            }
        }

        // 计算从startIndex到currentIndex之间所有type非1的预算总和
        for (int i = startIndex; i < currentIndex; i++) {
            Budget budget = allBudgets.get(i);
            // 只计算type为0（普通类型）的预算
            if (budget.getType() != null && budget.getType() == 0 && budget.getBudget() != null) {
                totalBudget = totalBudget.add(budget.getBudget());
                // 记录起始行信息（第一个遇到的type=0的行）
                if (startRowId == null) {
                    startRowId = budget.getRowId();
                }

                // 更新结束行信息（最后一个遇到的type=0的行）
                endRowId = budget.getRowId();
            }
        }

        return BudgetCalculationVo.builder()
                .totalBudget(totalBudget)
                .startRowId(startRowId)
                .endRowId(endRowId)
                .build();
    }

}
